---
title: 策略模式（上）
description: 如何避免冗长的if-else/switch分支判断代码
---

策略模式最常见的应用场景是，利用它来避免冗长的 if-else 或 switch 分支判断。也可以像 模板模式那样，提供框架的扩展点等等。

## 策略模式的原理与实现

策略模式，英文全称是 Strategy Design Pattern。它的定义是：定义一个算法族，分别封装起来，让它们之间可以互相替换。策略模式的主要目的是让算法的变化独立于使用算法的客户。

工厂模式是解耦对象的创建和使用，观察者模式解耦观察者和被观察者，策略模式是解耦策略的定义、创建和使用。

### 1. 策略的定义

策略的定义比较简单，包含一个策略接口和一组实现这个接口的策略类。策略接口定义了一个策略族的抽象，而策略类则封装了具体的算法和行为，继承自策略接口。

```java title='示例代码'
public interface Strategy {
    void algorithmInterface();
}

public class ConcreteStrategyA implements Strategy {
    @Override
    public void algorithmInterface() {
        //...
    }
}

public class ConcreteStrategyB implements Strategy {
    @Override
    public void algorithmInterface() {
        //...
    }
}
```

### 2. 策略的创建

因为策略模式会包含一组策略，在使用它们的时候，一般会通过类型（type）来判断创建那个策略来使用。所以，我们需要一个策略工厂来创建策略（对客户端代码屏蔽创建细节）。

```java title='如果策略类是无状态的，不包含成员变量，只是纯粹的算法实现，这样的策略对象是可以被共享使用的，不需要每次调用 getStrategy() 的时候，都创建一个新的策略对象。'
public class StrategyFactory {
    private static final Map<String, Strategy> strategies = new HashMap<>();

    static {
        strategies.put("A", new ConcreteStrategyA());
        strategies.put("B", new ConcreteStrategyB());
    }

    public static Strategy getStrategy(String type) {
        if (type == null || type.isEmpty()) {
            throw new IllegalArgumentException("type should not be empty.");
        }
        return strategies.get(type);
    }
}
```

```java title='如果策略类是有状态的，根据业务场景的需要，每次调用 getStrategy() 的时候，都创建一个新的策略对象。'
public class StrategyFactory {
    public static Strategy getStrategy(String type) {
        if (type == null || type.isEmpty()) {
            throw new IllegalArgumentException("type should not be empty.");
        }
        if (type.equals("A")) {
            return new ConcreteStrategyA();
        } else if (type.equals("B")) {
            return new ConcreteStrategyB();
        }
        return null;
    }
}
```

### 3. 策略的使用

策略模式包含一组可选策略，客户端代码一般会持有一个策略类的引用，用来执行具体的策略。客户端代码如何确定使用哪个策略呢？策略模式提供了两种方式：客户端代码自己决定、客户端代码通过参数告诉策略工厂。

最常见的就是在运行时，根据某些条件，动态地去选择、创建、使用具体的策略。这是策略模式最典型的应用场景。

```java title='运行时动态确定：事先并不知道会使用那个策略，而是在程序的运行期间，根据配置、用户输入、计算结果等这些不确定的因素，动态决定使用那种策略。'
//策略接口：EvictionStrategy
//策略类： LruEvictionStrategy、FifoEvictionStrategy、LfuEvictionStrategy
//策略工厂：EvictionStrategyFactory
public class UserCache {
    private Map<String, User> cacheData = new HashMap<>();
    private EvictionStrategy evictionStrategy;

    public UserCache(EvictionStrategy evictionStrategy) {
        this.evictionStrategy = evictionStrategy;
    }
}

//运行时动态确定，根据配置文件的配置决定使用那种策略
public class Application {
    public static void main(String[] args) {
        EvictionStrategy evictionStrategy = EvictionStrategyFactory.getEvictionStrategy(config);
        UserCache userCache = new UserCache(evictionStrategy);
    }
}

//非运行时动态确定，在代码中指定使用那种策略
public class Application {
    public static void main(String[] args) {
        EvictionStrategy evictionStrategy = new LruEvictionStrategy();
        UserCache userCache = new UserCache(evictionStrategy);
    }
}
```
非运行时动态确定，并不能发挥出策略模式的优势，在这种场景下，策略模式实际上退化成了“面向接口而非实现的变成原则”

